//
// Created by Administrator on 2020/12/24.
//
#include <jni.h>
#include <stdio.h>
#include<android/log.h>
//https://blog.csdn.net/dajian790626/article/details/9952711?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
#define  LOGI(...) __android_log_print(ANDROID_LOG_INFO, "========= Info =========   ", __VA_ARGS__)

#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, "========= Error =========   ", __VA_ARGS__)

#define  LOGD(...)  __android_log_print(ANDROID_LOG_INFO, "========= Debug =========   ", __VA_ARGS__)

#define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN, "========= Warn =========   ", __VA_ARGS__)

//测试局部引用
void testLocalRef(jint javaIntData){
    LOGD("testLocalRef: %d", javaIntData);
}

//c中拿到java的字符串并且返回字符串
JNIEXPORT jstring JNICALL
Java_com_endoc_jni2learn_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz, jstring input) {
    // TODO: implement stringFromJNI()
    char buf[1024];
    const char *name =NULL;
    jboolean iscopy;//有没有拷贝成功
    //因为c层只有字符数组,所以需要将java层的jstring转换为字符数组
   name=(*env)->GetStringUTFChars(env, input, &iscopy);
    printf("iscopy===%d",iscopy);
   if(name==NULL){//如果申请失败一定要返回null,不然会导致程序崩溃
       return  NULL;
   }
    printf("name===%s",name);
   //拷贝字符串
    sprintf(buf,"%s 我是c层的字符串", name);
    printf("C_str: %s \n", buf);
    /*在调用GetStringUTFChars函数从JVM内部获取一个字符串之后，JVM内部会分配一块新的内存，用于存储源字符串
    的拷贝，以便本地代码访问和修改。即然有内存分配，用完之后马上释放是一个编程的好习惯。通过调用
    ReleaseStringUTFChars函数通知JVM这块内存已经不使用了，你可以清除了。注意：这两个函数是配对使用的，用
    了GetXXX就必须调用ReleaseXXX，而且这两个函数的命名也有规律，除了前面的Get和Release之外，后面的都一
    样
     而c层的jstring input也是虚拟机内部分配的内存,和原先java层传入的内存没有关系了,所以也需要释放
     */
    (*env)->ReleaseStringUTFChars(env, input, name);
    return (*env)->NewStringUTF(env,buf);
}

//c调用java层的方法和属性,反射拿的
JNIEXPORT void JNICALL
Java_com_endoc_jni2learn_MainActivity_functionAndPropertyFromJNI(JNIEnv *env, jobject thiz) {
    // TODO: implement functionAndPropertyFromJNI()
    jclass  jclass1 =(*env)->GetObjectClass(env,thiz);//拿到类的字节码文件

    //TODO 拿到非静态属性进行读写
    jfieldID  jfieldId=(*env)->GetFieldID(env,jclass1,"property","I");//拿到类的非静态属性.属性名为property,类型为"I",对应java中的int
    jint javaIntData = (*env)->GetIntField(env,thiz,jfieldId);//拿到这个属性对应的值
    testLocalRef(javaIntData);
    LOGD("非静态变量: %d", javaIntData);
    (*env)->SetIntField(env, thiz, jfieldId, javaIntData + 10086);//修改java层的值
    LOGD("修改后非静态变量: %d", javaIntData);

    //TODO 拿到静态属性进行读写
    jfieldID  jstaticfieldId = (*env)->GetStaticFieldID(env,jclass1,"staticproperty", "I");
    jint javastaticIntData = (*env)->GetStaticIntField(env,jclass1,jstaticfieldId);//拿到这个属性对应的值
    LOGD("静态变量: %d", javastaticIntData);
    //修改静态变量,此处修改C层后需要重新去读取java的值才能得到修改后的值
    (*env)->SetStaticIntField(env,jclass1,jstaticfieldId,10);
    LOGD("修改后静态变量: %d", javastaticIntData);

   /*TODO 打印结果
    2020-12-24 16:20:54.553 8409-8409/com.endoc.jnilearntest I/========= Debug =========: 非静态变量: 1
    2020-12-24 16:20:54.553 8409-8409/com.endoc.jnilearntest I/========= Debug =========: 修改后非静态变量: 1
    2020-12-24 16:20:54.553 8409-8409/com.endoc.jnilearntest I/========= Debug =========: 静态变量: 2
    2020-12-24 16:20:54.553 8409-8409/com.endoc.jnilearntest I/========= Debug =========: 修改后静态变量: 2
    2020-12-24 16:20:54.553 8409-8409/com.endoc.jnilearntest I/System.out: function fun:10
    2020-12-24 16:20:54.553 8409-8409/com.endoc.jnilearntest D/JNIMainActivity: java层修改后property值:10087*/

    //TODO 拿到非静态方法
    /*
     *  public int function(int fun, Date date, int[] arr){
        System.out.println("function");
        return 0;
    }
     (ILjava/util/Date;[I)I
        I表示第一个参数int类型
        Ljava/util/Date;表示Data参数,此处注意;是用来类全包名的
        [I表示数组
     * */
    jmethodID jmethodId =  (*env)->GetMethodID(env,jclass1,"function","(ILjava/util/Date;[I)I");//得到方法类对象

    //TODO 调用方法
    (*env)->CallIntMethod(env,thiz,jmethodId,10L,NULL,NULL);

    //TODO 拿到静态方法
    jmethodID jstaticmethodId =  (*env)->GetStaticMethodID(env,jclass1,"functionStaticFromJNI", "(I)V");
    //TODO 调用静态方法,此处返回值是void所以调用void方法
    (*env)->CallStaticVoidMethod(env,jclass1,jstaticmethodId,1254);


    //TODO 以下是调用构造方法
    //TODO 拿到类的字节码文件
    jclass  jconstructclass =(*env)->FindClass(env,"com/endoc/jni2learn/JniConstruct");
    if (jconstructclass == NULL) {
        printf("dateclz getclass null");
        return ;
    }
    //TODO 得到构造方法 <init>字段就表示构造方法,规定的,此处需GetMethodID,表示调用构造函数
    jmethodID  jconstructmethodId=(*env)->GetMethodID(env,jconstructclass,"<init>", "()V");
    if (jconstructmethodId == NULL) {
        printf("method_id GetMethodID null");
        return ;
    }
    //TODO 创建实例对象,会自动调用构造方法
    jobject  date = (*env)->NewObject(env, jconstructclass,jconstructmethodId);


}


//直接拿到java层的内存进行操作,而不是本地创建一个内存进行拷贝
JNIEXPORT void JNICALL
Java_com_endoc_jni2learn_MainActivity_intArrayFromJNI(JNIEnv *env, jobject thiz, jintArray a) {
    jboolean  jboolean1;
    int * intArray;
    //直接拿到java层的引用,jni本地不进行拷贝,操作的都是java层的内存空间
    intArray = (*env)->GetPrimitiveArrayCritical(env,a,&jboolean1);
    *(intArray++)=10;

   /* 释放jni对java层的引用,否则会导致内存泄露,
    mode可以取下面的值:
    0 ：对Java的数组进行更新并释放C/C++的数组
    JNI_COMMIT ：对Java的数组进行更新但是不释放C/C++的数组
    JNI_ABORT：对Java的数组不进行更新,释放C/C++的数组*/
    (*env)->ReleasePrimitiveArrayCritical(env,a, intArray ,0);
    //return intArray;
   // return  (*env)->GetIntArrayElements(env,a,&jboolean1);
}